package com.maegul.data.dao;

import java.util.List;

import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;

import com.maegul.data.entities.AbstractEntity;

/**
 * Clase abstracta que modela el comportamiento basico de el DAO usado en la
 * aplicacion
 * 
 * @param <EntityClass>
 *            Entidad que modela esta clase
 * @see com.maegul.data.dao.DAO
 */
public abstract class AbstractDAO<EntityClass extends AbstractEntity>
		implements DAO<EntityClass> {

	private static Logger LOG = Logger.getLogger(AbstractDAO.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#findAll()
	 */
	@SuppressWarnings("unchecked")
	public List<EntityClass> findAll() {
		Query query = getFindAllQuery();
		return query.getResultList();
	}

	protected Query getFindAllQuery() {
		return getEntityManager().createQuery(getBaseQueryBuilder().toString());
	}

	private StringBuilder getBaseQueryBuilder() {
		StringBuilder queryBuilder = new StringBuilder("select entity from ");
		queryBuilder.append(getClazz().getName());
		queryBuilder.append(" entity");
		return queryBuilder;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#findById(int)
	 */
	public EntityClass findById(int id) {
		return getEntityManager().find(getClazz(), id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#findByProperty(java.lang.String,
	 * java.lang.Object)
	 */
	public EntityClass findByProperty(String propertyName, Object propertyValue) {

		if (propertyName == null || propertyValue == null) {
			throw new IllegalArgumentException(
					"Property name and value cannot be null! Values: propertyName"
							+ propertyName + ", propertyValue = "
							+ propertyValue);
		}

		CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
		CriteriaQuery<EntityClass> findEntityQuery = cb.createQuery(getClazz());
		Root<EntityClass> entity = findEntityQuery.from(getClazz());

		Predicate propertyPredicate = cb.equal(entity.get(propertyName),
				propertyValue);

		findEntityQuery.where(propertyPredicate);

		TypedQuery<EntityClass> query = getEntityManager().createQuery(
				findEntityQuery);
		List<EntityClass> entities = query.setMaxResults(1).getResultList();

		if (entities.isEmpty()) {
			return null;
		} else {
			return entities.get(0);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#persist(java.lang.Object)
	 */
	public void persist(EntityClass entity) {
		getEntityManager().persist(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#remove(java.lang.Object)
	 */
	public void remove(EntityClass entity) {
		getEntityManager().remove(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#removeSafely(java.lang.Object)
	 */
	public void removeSafely(EntityClass entity) {
		getEntityManager().remove(
				getEntityManager().getReference(getClazz(), entity.getId()));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#getReference(java.lang.Object)
	 */
	public EntityClass getReference(EntityClass entity) {

		if (entity.isNew()) {
			return entity;
		} else {
			return getEntityManager().getReference(getClazz(), entity.getId());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#size()
	 */
	public long size() {
		StringBuilder query = new StringBuilder("select count(entity) from ");
		query.append(getClazz().getName());
		query.append(" entity");

		Long result = (Long) getEntityManager().createQuery(query.toString())
				.getSingleResult();
		return result.longValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.maegul.data.dao.DAO#merge(java.lang.Object)
	 */
	public EntityClass merge(EntityClass entity) {
		return getEntityManager().merge(entity);
	}
}
